#include "EP3DRect.h"

namespace EP3D
{
	Rect::Rect()
		:left(0)
		,top(0)
		,right(0)
		,bottom(0)
	{}

	Rect::Rect(f32 l, f32 t, f32 r, f32 b)
		:left(l)
		,top(t)
		,right(r)
		,bottom(b)
	{}

	Rect::Rect(const Rect &other)
		:left(other.left)
		,top(other.top)
		,right(other.right)
		,bottom(other.bottom)
	{}

	Rect::Rect(Point point, Size size)
	{
		left = point.x;
		top = point.y;
		right = left + size.width;
		bottom = top + size.height;
	}

	Rect::Rect(Point topLeft, Point bottomRight)
	{
		left = topLeft.x;
		top = topLeft.y;
		right = bottomRight.x;
		bottom = bottomRight.y;
	}

	Point Rect::BottomRight() const
	{
		return Point(right, bottom);
	}

	Point Rect::CenterPoint() const
	{
		return Point(
			(left + right) * 0.5f,
			(right + bottom) * 0.5f);
	}

	void Rect::CopyRect(const Rect& rect)
	{
		left = rect.left;
		top = rect.top;
		right = rect.right;
		bottom = rect.bottom;
	}

	void Rect::DeflateRect(f32 x, f32 y)
	{
		left += x;
		right -= x;
		top += y;
		bottom -= y;
	}

	void Rect::DeflateRect(Size size)
	{
		left += size.width;
		right -= size.width;
		top += size.height;
		bottom -= size.height;
	}

	void Rect::DeflateRect(const Rect& rect)
	{
		left += rect.left;
		right -= rect.right;
		top += rect.top;
		bottom -= rect.bottom;
	}

	void Rect::DeflateRect(f32 l, f32 t, f32 r, f32 b)
	{
		left += l;
		top += t;
		right -= r;
		bottom -= b;
	}

	f32 Rect::Height() const
	{
		return bottom - top;
	}

	void Rect::InflateRect(f32 x, f32 y)
	{
		left -= x;
		right += x;
		top -= y;
		bottom += y;
	}

	void Rect::InflateRect(Size size)
	{
		left -= size.width;
		right += size.width;
		top -= size.height;
		bottom += size.height;
	}

	void Rect::InflateRect(const Rect &rect)
	{
		left -= rect.left;
		right += rect.right;
		top -= rect.top;
		bottom += rect.bottom;
	}

	void Rect::InflateRect(f32 l, f32 t, f32 r, f32 b)
	{
		left -= l;
		top -= t;
		right += r;
		bottom += b;
	}

	bool Rect::IntersectRect(const Rect& rect1, const Rect& rect2)
	{
		if ((rect1.right > rect2.left) &&
			(rect1.left < rect2.right) &&
			(rect1.top < rect2.bottom) &&
			(rect1.bottom > rect2.top))
		{
			left = (rect1.left > rect2.left) ? rect1.left : rect2.left;
			right = (rect1.right < rect2.right) ? rect1.right : rect2.right;
			top = (rect1.top > rect2.top) ? rect1.top : rect2.top;
			bottom = (rect1.bottom < rect2.bottom) ? rect1.bottom : rect2.bottom;

			return true;
		}
		else
		{
			left = top = right = bottom = 0.0f;
			return false;
		}
	}

	bool Rect::IsRectEmpty() const
	{
		return (((right - left) <= DELTA) || ((bottom - top) <= DELTA)); 
	}

	bool Rect::IsRectNull() const
	{
		return ((left <= DELTA) &&
			(right <= DELTA) &&
			(top <= DELTA) &&
			(bottom <= DELTA));
	}

	void Rect::MoveToX(f32 x)
	{
		f32 width = Width();
		left = x;
		right = left + width;
	}

	void Rect::MoveToXY(f32 x, f32 y)
	{
		Size size = GetSize();
		left = x;
		right = left + size.width;
		top = y;
		bottom = top + size.height;
	}

	void Rect::MoveToXY(Point point)
	{
		Size size = GetSize();
		left = point.x;
		right = left + size.width;
		top = point.y;
		bottom = top + size.height;
	}

	void Rect::MoveToY(f32 y)
	{
		f32 height = Height();
		top = y;
		bottom = top + height;
	}

	void Rect::NormalizeRect()
	{
		if (left > right)
			Mathf::Swap(left, right);

		if (top > bottom)
			Mathf::Swap(top, bottom);
	}

	void Rect::OffsetRect(f32 x, f32 y)
	{
		Size size = GetSize();
		left = x;
		top = y;
		right = left + size.width;
		bottom = top + size.height;
	}

	void Rect::OffsetRect(Point point)
	{
		Size size = GetSize();
		left = point.x;
		top = point.y;
		right = left + size.width;
		bottom = top + size.height;
	}

	void Rect::OffsetRect(Size size)
	{
		Size s = GetSize();
		left = size.width;
		top = size.height;
		right = left + s.width;
		bottom = top + s.height;
	}

	bool Rect::PtInRect(Point point) const
	{
		return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom;
	}

	void Rect::SetRect(f32 l, f32 t, f32 r, f32 b)
	{
		left = l;
		top = t;
		right = r;
		b = bottom;
	}

	void Rect::SetRect(Point point, Size size)
	{
		left = point.x;
		top = point.y;
		right = left + size.width;
		bottom = top + size.height;
	}

	void Rect::SetRectEmpty()
	{
		left = top = right = bottom = 0.0f;
	}

	Size Rect::GetSize() const
	{
		return Size(right - left, bottom - top);
	}

	Point Rect::TopLeft() const
	{
		return Point(left, top);
	}

	bool Rect::UnionRect(const Rect& rect1, const Rect& rect2)
	{
		left = (rect1.left < rect2.left) ? rect1.left : rect2.left;
		right = (rect1.right > rect2.right) ? rect1.right : rect2.right;
		top = (rect1.top < rect2.top) ? rect1.top : rect2.top;
		bottom = (rect1.bottom > rect2.bottom) ? rect1.bottom : rect2.bottom;

		return IsRectEmpty() == true;
	}

	f32 Rect::Width() const
	{
		return right - left;
	}

	Rect Rect::operator + (Point point) const
	{
		Rect ret(*this);
		return ret += point;
	}

	Rect Rect::operator - (Point point) const
	{
		Rect ret(*this);
		return ret -= point;
	}

	Rect& Rect::operator +=(Point point)
	{
		OffsetRect(point);
		return *this;
	}

	Rect& Rect::operator -=(Point point)
	{
		OffsetRect(-point);
		return *this;
	}

	Rect Rect::operator *(f32 scale) const
	{
		return Rect(left * scale, top * scale, right * scale, bottom * scale);
	}

	const Rect& Rect::operator *=(f32 scale)
	{
		left *= scale;
		top *= scale;
		right *= scale;
		bottom *= scale;
		return *this;
	}

	bool Rect::operator !=(const Rect &rect) const
	{
		return ((left != rect.left) ||
			(top != rect.top) ||
			(right != rect.right) ||
			(bottom != rect.bottom));
	}

	bool Rect::operator ==(const Rect &rect) const
	{
		return ((Mathf::Abs(left - rect.left) < DELTA) &&
			(Mathf::Abs(top - rect.top) < DELTA) &&
			(Mathf::Abs(right - rect.right) < DELTA) &&
			(Mathf::Abs(bottom - rect.bottom) < DELTA));
	}
}
